import emojiRegex from "emoji-regex";
import mime from "mime-types";
import truncate from "lodash/truncate";
import parseTitle from "@shared/utils/parseTitle";
import { DocumentValidation } from "@shared/validations";
import { traceFunction } from "@server/logging/tracing";
import { User } from "@server/models";
import { ProsemirrorHelper } from "@server/models/helpers/ProsemirrorHelper";
import { TextHelper } from "@server/models/helpers/TextHelper";
import { APIContext } from "@server/types";
import { DocumentConverter } from "@server/utils/DocumentConverter";
import { InvalidRequestError } from "../errors";

type Props = {
  user: User;
  mimeType: string;
  fileName: string;
  content: Buffer | string;
  ctx: APIContext;
};

async function documentImporter({
  mimeType,
  fileName,
  content,
  user,
  ctx,
}: Props): Promise<{
  icon?: string;
  text: string;
  title: string;
  state: Buffer;
}> {
  let text = await DocumentConverter.convertToMarkdown(
    content,
    fileName,
    mimeType
  );

  // find valid extensions and remove them from the title
  const extensions = [
    "docx",
    "md",
    "markdown",
    "html",
    ...(mime.extensions[mimeType] ?? []),
  ];
  let title = fileName.replace(
    new RegExp(`\\.(${extensions.join("|")})$`, "i"),
    ""
  );

  // find and extract emoji near the beginning of the document.
  const regex = emojiRegex();
  const matches = regex.exec(text.slice(0, 10));
  const icon = matches ? matches[0] : undefined;
  if (icon) {
    text = text.replace(icon, "");
  }

  // If the first line of the imported text looks like a markdown heading
  // then we can use this as the document title rather than the file name.
  if (text.startsWith("# ")) {
    const result = parseTitle(text);
    title = result.title;
    text = text.replace(/^.+(\n|$)/, "");
  }

  // Replace any <br> generated by the turndown plugin with escaped newlines
  // to match our hardbreak parser.
  text = text.trim().replace(/<br>/gi, "\\n");

  // Remove any closed and immediately reopened formatting marks
  text = text.replace(/\*\*\*\*/gi, "").replace(/____/gi, "");

  text = await TextHelper.replaceImagesWithAttachments(ctx, text, user);

  // Sanity check – text cannot possibly be longer than state so if it is, we can short-circuit here
  if (text.length > DocumentValidation.maxStateLength) {
    throw InvalidRequestError(
      `The document "${title}" is too large to import, please reduce the length and try again`
    );
  }

  // It's better to truncate particularly long titles than fail the import
  title = truncate(title, { length: DocumentValidation.maxTitleLength });

  const ydoc = ProsemirrorHelper.toYDoc(text);
  const state = ProsemirrorHelper.toState(ydoc);

  if (state.length > DocumentValidation.maxStateLength) {
    throw InvalidRequestError(
      `The document "${title}" is too large to import, please reduce the length and try again`
    );
  }

  return {
    text,
    state,
    title,
    icon,
  };
}

export default traceFunction({
  spanName: "documentImporter",
})(documentImporter);
